#ifndef __ASTRO_H__
#define __ASTRO_H__

#include "calibrater.h"
#include "goto.h"
#include "base.pb.h"
#include "astro.pb.h"
#include "album_common.h"
#include "image_processor/image_processor.h"
#include "libhv/json.hpp"
#include "ws_constant.h"
#include "sky_seacher/sky_seacher.h"
#include "album.h"
#include "rgb_power.h"

enum RawFormat {
    FITS,
    TIFF
};

struct DarkFrameParameter
{
    double exp;
    int gain;
    int bin;

    // 重载运算符以进行元素比较
    bool operator==(const DarkFrameParameter& other) const {
        return (exp == other.exp) && (gain == other.gain) && (bin == other.bin);
    }
};

class ITips;

class Astro {
private:
    CamTele cam_tele_;
    FileManager file_manager_;
    ImageProcessor image_processor_;
    SkySeacher sky_seacher_;
    Album album_;
    RgbPower rgb_power_;

    std::future<int> capture_raw_thread_;
    std::future<void> live_stacking_thread_;

    std::atomic<bool> b_capture_dark_ = false;

    // SD卡只存一组暗场
    const std::string sd_dark_path_ = album_path + astro_path + "DWARF_DARK/";

    // 要拍的暗场库参数
    const std::vector<DarkFrameParameter> dark_lib_ = 
    {{15.0, 30, 0}, {15.0, 40, 0}, {15.0, 50, 0}, {15.0, 60, 0}, {15.0, 70, 0}, {15.0, 80, 0}, {15.0, 90, 0}, {15.0, 100, 0}, {15.0, 110, 0}, {15.0, 120, 0}, {15.0, 130, 0}, {15.0, 140, 0}, {15.0, 150, 0},
     {15.0, 30, 1}, {15.0, 40, 1}, {15.0, 50, 1}, {15.0, 60, 1}, {15.0, 70, 1}, {15.0, 80, 1}, {15.0, 90, 1}, {15.0, 100, 1}, {15.0, 110, 1}, {15.0, 120, 1}, {15.0, 130, 1}, {15.0, 140, 1}, {15.0, 150, 1}};

    // 一组暗场拍3张
    const int dark_count = 3;

    // 用于同步APP暗场进度和剩余时间
    std::atomic<double> capture_dark_progress_ = 0.0;
    std::atomic<int> capture_dark_remaining_time_s = 0;

    static std::atomic<StateNotify> capture_dark_state_;
    static std::atomic<StateNotify> capture_light_state_;

    // 亮场拍摄参数
    static std::atomic<int> capture_light_to_take_;
    static std::atomic<int> capture_light_binning_;
    static std::atomic<int> capture_light_format_;

    // 用于同步APP亮场进度
    std::atomic<int> capture_light_img_taken_ = 0;
    int capture_light_img_to_take_ = 0;
    int capture_light_exp_setting_index_ = 0;
    int capture_light_gain_setting_index_ = 0;
    std::string capture_light_target_name_;

    ITips* itips_observer_;

public:
    Calibrater calibrater_; 
    Goto gotoer_;
    
private:
    Astro() {}

    void setAndNotifyCaptureLightState(const StateNotify& state);
    void notifyCaptureLightStateAndProgress();
    void setAndNotifyCaptureDarkState(const StateNotify& state);
    void notifyCaptureDarkStateAndProgress();

    void setAndNotifyCaptureLightImgTaken(int img_count);
    void notifyCaptureLightImgTaken();
    void setAndNotifyCaptureDarkProgress(double progress, int remain_time_s);
    void notifyCaptureDarkProgress();

public:
    static Astro& astro() { 
        static Astro astro;  
        return astro;
    }

    int handleMessage(WsPacket& ws_packet);

    void notifyAstroStateAndProgress();

    int panoMosaic();

    int oneClickGoto(WsPacket& ws_packet);

    int calibration(WsPacket& ws_packet);
    int calibration();

    int stopCalibration(WsPacket& ws_packet);
    int stopCalibration();

    int setGpsLocation(WsPacket& ws_packet);
    int setGpsLocation(const GeocentricCoordinates& gps_position);

    int gotoAndTrackDSO(WsPacket& ws_packet);
    int gotoAndTrackTarget(const DeepSkyObject& target);
    int gotoAndTrackSolarSystem(WsPacket& ws_packet);
    int gotoAndTrackTarget(const SolarGalaxyObject& target);

    int stopGotoAndTracking(WsPacket& ws_packet);
    int stopGotoAndTracking();

    int trackSpecialTarget(WsPacket& ws_packet);
    int stopTrackingSpecialTarget(WsPacket& ws_packet);

    int captureRawLiveStacking(WsPacket& ws_packet);
    int captureRawLiveStacking(int binning, int img_to_take, int format);

    int stopCaptureRawLiveStacking(WsPacket& ws_packet);
    int stopCaptureRawLiveStacking();

    int goLive(WsPacket& ws_packet);

    int captureDark(WsPacket& ws_packet);
    int captureDark(const std::vector<DarkFrameParameter>& dark_lost);

    int stopCaptureDark(WsPacket& ws_packet);
    int stopCaptureDark();

    int checkGotDark(WsPacket& ws_packet);
    int checkGotDark(std::vector<DarkFrameParameter>& dark_got);
    int checkLostDark(std::vector<DarkFrameParameter>& dark_lost);

    inline void setCaptureLightCount(int capture_light_to_take) { capture_light_to_take_ = capture_light_to_take; }
    inline void setCaptureLightBinning(int capture_light_binning) { capture_light_binning_ = capture_light_binning; }
    inline void setCaptureLightFormat(int capture_light_format) { capture_light_format_ = capture_light_format; }
    inline void setCaptureLightDisplaySource(int capture_light_display_source) { image_processor_.setCaptureLightDisplaySource(capture_light_display_source); }

    inline int getCaptureLightCount() { return capture_light_to_take_; }
    inline int getCaptureLightBinning() { return capture_light_binning_; }
    inline int getCaptureLightFormat() { return capture_light_format_; }
    inline int getCaptureLightDisplaySource() { return image_processor_.getCaptureLightDisplaySource(); }

    inline StateNotify getCaptureDarkState() { return capture_dark_state_; }
    inline StateNotify getCaptureLightState() { return capture_light_state_; }
    inline StateNotify getAstroState() { 
        if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE || gotoer_.getTrackerState() != STATE_IDLE ||
            capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE)
            return STATE_RUNNING;
        return STATE_IDLE;
    }
    inline StateNotify getAstroStateWithoutTrackerState() { 
        if (calibrater_.getCalibraterState() != STATE_IDLE || gotoer_.getGotoerState() != STATE_IDLE || 
            capture_dark_state_ != STATE_IDLE || capture_light_state_ != STATE_IDLE)
            return STATE_RUNNING;
        return STATE_IDLE;
    }

    inline void addITipsObserver(ITips* itips_observer) { 
        itips_observer_ = itips_observer; 
        calibrater_.addITipsObserver(itips_observer_);
        gotoer_.addITipsObserver(itips_observer_);
    }
};



#endif